package com.thiswhyme.gatewayserver.utils;

import com.thiswhyme.common.expection.BusinessException;
import com.thiswhyme.common.response.ResponseCode;
import com.thiswhyme.gatewayserver.dto.Constants;
import io.jsonwebtoken.*;
import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SignatureException;
import java.util.Date;
import java.util.HashMap;

/**
 * @author thiswhyme 2020/6/1
 */
public class JwtHelper {

    /**
     * 生成签名的时候使用的秘钥secret
     * 这里为了方便通过方法封装，一般可以从本地配置文件中读取
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.decodeBase64(Constants.JWT_SECRET);
        return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
    }

    /**
     * 验证JWT
     * @param jwtStr
     * @return
     */
    public static Claims validateJwt(String jwtStr) {
        Claims result = null;
        try {
            result = parseJWT(jwtStr);
        } catch (ExpiredJwtException e) {
            throw new BusinessException(ResponseCode.OPERATION_FAILURE.getCode(), "Token已过期");
        } catch (SignatureException e) {
            throw new BusinessException(ResponseCode.OPERATION_FAILURE.getCode(), "Token错误");
        } catch (Exception e) {
            throw new BusinessException(ResponseCode.OPERATION_FAILURE.getCode(), "Token校验失败");
        }
        return result;
    }

    /**
     * 创建jwt
     * @param id
     * @param subject
     * @param ttlMillis
     * @return
     * @throws Exception
     */
    public static String createJWT(String id, String subject, long ttlMillis) {
        long nowMillis = System.currentTimeMillis();
        JwtBuilder builder = Jwts.builder()
                .setClaims(new HashMap<>(16)) //设置私有的声明，可放置自定义数据
                .setId(id) //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setIssuer(Constants.SIGN_ISSUER)// 签发者
                .setIssuedAt(new Date(System.currentTimeMillis())) //生成JWT的时间
                .setSubject(subject) //sub(Subject)：代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .signWith(SignatureAlgorithm.HS256, generalKey()); //设置签名算法和签名使用的秘钥
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);
        }
        return builder.compact();
    }

    /**
     * 解密
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) throws Exception {
        return Jwts.parser()
                .setSigningKey(generalKey())
                .parseClaimsJws(jwt).getBody();
    }
}
